package database

import (
	"github.com/jmoiron/sqlx"
	"learning/gooop/saga/mqs/logger"
)
import _ "github.com/mattn/go-sqlite3"

type DBFunc func(db *sqlx.DB) error
type TXFunc func(db *sqlx.DB, tx *sqlx.Tx) error

func init() {
	// must prepare tables
	err := DB(initDB)
	if err != nil {
		panic(err)
	}
}

func initDB(db *sqlx.DB) error {
	// 订阅者/消费者: subscriber
	_, e := db.Exec(`create table if not exists subscriber(
	ID integer primary key autoincrement,
	ClientID varchar(50) not null,
	Topic varchar(100) not null,
	NotifyURL varchar(500) not null,
    ExpireTime integer
)`)
	if e != nil {
		return e
	}

	_, e = db.Exec("create unique index if not exists udx_subscriber on subscriber(ClientID, topic)")
	if e != nil {
		return e
	}

	// 事务消息: tx_msg
	_, e = db.Exec(`create table if not exists tx_msg (
    ID integer primary key autoincrement,
    GlobalID string varchar(50) not null,
    SubID string varchar(50) unique not null,
    SenderID varchar(50) not null,
    CreateTime integer not null,
    Topic varchar(100) not null,
    Content nvarchar(2048) not null
)`)
	if e != nil {
		return e
	}

	// 投递队列: delivery_queue
	_, e = db.Exec(`create table if not exists delivery_queue (
	ID integer primary key autoincrement,
	
	ClientID varchar(50) not null,
	NotifyURL varchar(500) not null,
    
    MsgID integer not null,
	GlobalID string varchar(50) not null,
    SubID string varchar(50) unique not null,
    SenderID varchar(50) not null,
    CreateTime integer not null,
    
    Topic varchar(100) not null,
    Content nvarchar(2048) not null,
    
	StatusFlag integer not null,
	UpdateTime integer not null
)`)
	if e != nil {
		return e
	}

	_, e = db.Exec("create index if not exists idx_delivery_queue_msg_id on delivery_queue(MsgID)")
	if e != nil {
		return e
	}

	_, e = db.Exec("create index if not exists idx_delivery_queue_client_id on delivery_queue(ClientID)")
	if e != nil {
		return e
	}

	// 成功投递队列: success_queue
	_, e = db.Exec(`create table if not exists success_queue (
	ID integer primary key autoincrement,
	MsgID integer not null,
	ClientID varchar(50) not null,
    CreateTime integer not null
)`)
	if e != nil {
		return e
	}

	return nil
}

func open() (*sqlx.DB, error) {
	return sqlx.Open("sqlite3", "./mqs.sqlite3")
}

func DB(action DBFunc) error {
	db,err := open()
	if err != nil {
		logger.Logf("database.open, err=%v", err)
		return err
	}
	defer func() { _ = db.Close() }()

	err = action(db)
	if err != nil {
		logger.Logf("database.DB, err=%v", err)
		return err
	} else {
		return nil
	}
}

func TX(action TXFunc) error {
	return DB(func(db *sqlx.DB) error {
		tx, err := db.Beginx()
		if err != nil {
			logger.Logf("database.TX, failed db.Beginx, err=%v", err)
			return err
		}

		err = action(db, tx)
		if err == nil {
			return tx.Commit()

		} else {
			_ = tx.Rollback()
			logger.Logf("database.TX, err=%v", err)
			return err
		}
	})
}